package com.md.util;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import com.md.entity.BaseEntity;
import com.md.exception.SystemException;
import com.md.exception.UnsupportedException;


/**
 * 持久化对象反射工具类。
 * 
 * @author yehao
 */
public class BeanAccessor {

	private static final Object[] EMPTY_ARGUMENTS = new Object[0];

	private static Map<Class<?>, BeanAccessor> cache = new HashMap<Class<?>, BeanAccessor>();

	private Class<?> beanClass = null;

	private Map<String, PropertyDescriptor> pdMap = new HashMap<String, PropertyDescriptor>();

	public static BeanAccessor getDefault(Class<?> beanClass) {
		if (cache.get(beanClass) == null) {
			BeanAccessor beanAccessor = new BeanAccessor(beanClass);
			cache.put(beanClass, beanAccessor);
		}
		return cache.get(beanClass);
	}

	public BeanAccessor(Class<?> beanClass) {
		this.beanClass = beanClass;
	}

	public Class<?> getBeanClass() {
		return this.beanClass;
	}

	/**
	 * 获取集合属性泛型的实际类型。（在声明属性时一定要设置了泛型才能取出来）
	 * 
	 * @param propertyName
	 *            集合属性。
	 * @return 集合属性泛型的实际类型。
	 */
	public Class<?> getCollectionPropertyGenericType(String propertyName) {
		try {
			Type type = this.beanClass.getDeclaredField(propertyName).getGenericType();
			if (type instanceof ParameterizedType) {
				return (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[0];
			}
		} catch (Exception e) {
			throw new SystemException(e);
		}
		return null;
	}

	/**
	 * 获取属性值。
	 * 
	 * @param obj
	 *            持久化对象。
	 * @param propertyName
	 *            属性名。
	 * @return 属性值。
	 */
	public Object getValue(Object obj, String propertyName) {
		PropertyDescriptor pd = this.getPropertyDescriptor(propertyName);
		if (pd != null) {
			try {
				return pd.getReadMethod().invoke(obj, EMPTY_ARGUMENTS);
			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				throw new SystemException(e);
			}
		} else if (obj instanceof BaseEntity) {
			return ((BaseEntity) obj).getExtendedValue(propertyName);
		}

		throw new UnsupportedException();
	}

	/**
	 * 设置属性值。
	 * 
	 * @param obj
	 *            持久化对象。
	 * @param propertyName
	 *            属性名。
	 * @param value属性值
	 *            。
	 */
	public void setValue(Object obj, String propertyName, Object value) {
		PropertyDescriptor pd = this.getPropertyDescriptor(propertyName);
		if (pd != null) {
			try {
				pd.getWriteMethod().invoke(obj, new Object[] { value });
			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				throw new SystemException(e);
			}
			return;
		} else if (obj instanceof BaseEntity) {
			((BaseEntity) obj).setExtendedValue(propertyName, value);
			return;
		}

		throw new UnsupportedException();
	}

	/**
	 * 获取属性描述对象。通过JAVA本身提供的属性描述对象进行反射，可以准确的生成JAVA标准的Getter,Setter名称。另外获取类型等也很方便。
	 * 
	 * @param propertyName
	 *            属性名。
	 * @return 属性描述对象。
	 * @throws IntrospectionException
	 */
	private PropertyDescriptor getPropertyDescriptor(String propertyName) {
		if (this.pdMap.containsKey(propertyName)) {
			return this.pdMap.get(propertyName);
		} else {
			PropertyDescriptor pd = null;
			try {
				pd = new PropertyDescriptor(propertyName, this.beanClass);
				this.pdMap.put(propertyName, pd);
			} catch (IntrospectionException e) {
				this.pdMap.put(propertyName, null);
			}
			return pd;
		}
	}

}
